package org.finesys.common.file.client.oss.service;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URL;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import org.apache.http.entity.ContentType;
import org.finesys.common.file.FileInfo;
import org.finesys.common.file.client.FileTemplate;
import org.finesys.common.file.client.oss.OssProperties;
import org.finesys.common.file.properties.FileProperties;
import org.springframework.http.HttpMethod;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.regions.RegionUtils;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.CreateBucketRequest;
import com.amazonaws.services.s3.model.DeleteBucketRequest;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ListBucketsPaginatedRequest;
import com.amazonaws.services.s3.model.ListBucketsPaginatedResult;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.amazonaws.util.IOUtils;

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;

@RequiredArgsConstructor
public class OssFileTemplate implements FileTemplate {

    private final FileProperties fileProperties;
    private AmazonS3 amazonS3;

    @Override
    public void createBucket(String bucketName) {
        if (!amazonS3.doesBucketExistV2(bucketName)) {
            amazonS3.createBucket(new CreateBucketRequest(bucketName));
        }
    }


    @Override
    public FileInfo putObject(String bucketName, String objectName, InputStream stream, String contentType) throws Exception {
        return putObject(bucketName, objectName, stream, stream.available(), contentType);
    }

    @Override
    public FileInfo putObject(String bucketName, String objectName, InputStream stream) throws Exception {
        return putObject(bucketName, objectName, stream, stream.available(), ContentType.APPLICATION_OCTET_STREAM.getMimeType());
    }

    public FileInfo putObject(String bucketName, String objectName, InputStream stream, int size, String contentType) throws Exception {
        byte[] bytes = IOUtils.toByteArray(stream);
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(size);
        objectMetadata.setContentType(contentType);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata);
        String url = getObjectUrl(bucketName, objectName);
        return new FileInfo()
                .setFileId(UUID.randomUUID().toString())
                .setFileName(objectName)
                .setOriginalFileName(objectName)
                .setBasePath(bucketName)
                .setUrl(url)
                .setSize(size)
                .setFileType(contentType);
    }


    @Override
    public void removeBucket(String bucketName) {
        amazonS3.deleteBucket(new DeleteBucketRequest(bucketName));
    }

    @Override
    public void removeObject(String bucketName, String objectName) throws Exception {
        amazonS3.deleteObject(bucketName, objectName);
    }

    @Override
    public S3Object getObject(String bucketName, String objectName) {
        return amazonS3.getObject(new GetObjectRequest(bucketName, objectName));
    }
    @SneakyThrows
    public Optional<Bucket> getBucket(String bucketName) {
        ListBucketsPaginatedRequest request=new ListBucketsPaginatedRequest();
        request.withBucketRegion(bucketName);
        request.withMaxBuckets(1);
        ListBucketsPaginatedResult listBucketsPaginatedResult=   amazonS3.listBuckets(request);
        return Optional.ofNullable(listBucketsPaginatedResult).map(ListBucketsPaginatedResult::getBuckets)
                .filter(list->!list.isEmpty()).map(list->list.get(0));

    }

    @Override
    public List<Bucket> getAllBuckets() {
        return amazonS3.listBuckets();
    }


    @Override
    public List<S3ObjectSummary> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
        ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix);
        return new ArrayList<>(objectListing.getObjectSummaries());
    }

    /**
     * 生成带过期时间的URL
     */
    public String getObjectUrl(String bucketName, String objectName) {
        URL url = amazonS3.getUrl(bucketName, objectName);
        return url.toString();
    }

    /**
     * 生成带过期时间的URL
     */
    public String getObjectUrl(String bucketName, String objectName, Integer expires) {
        Date date = new Date();
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, expires);
        URL url = amazonS3.generatePresignedUrl(bucketName, objectName, calendar.getTime());
        return url.toString();
    }

    /**
     * 获取共享文件外链 (GET请求)
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return url
     * {@link AmazonS3#generatePresignedUrl(String bucketName, String key, Date expiration)}
     * @see <a href=
     * "https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html"/>
     */
    @Override
    public String shareObjectPreSignedURL(String bucketName, String objectName) {
        return shareObjectPreSignedURL(bucketName, objectName, 7);
    }

    /**
     * 获取共享文件外链 (GET请求)
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param expires    过期时间 <=7天
     * @return url
     * {@link AmazonS3#generatePresignedUrl(String bucketName, String key, Date expiration)}
     * @see <a href=
     * "https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html"/>
     */
    @Override
    public String shareObjectPreSignedURL(String bucketName, String objectName, Integer expires) {
        return shareObjectPreSignedURL(bucketName, objectName, expires, null, HttpMethod.GET);
    }

    /**
     * 获取文件外链 (GET、PUT请求)
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param expires    过期时间 <=7天
     * @param params     请求参数
     * @param httpMethod GET、PUT请求
     * @return url
     * {@link AmazonS3#generatePresignedUrl(String bucketName, String key, Date expiration)}
     * @see <a href=
     * "https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html"/>
     */
    public String shareObjectPreSignedURL(String bucketName, String objectName, Integer expires, Map<String, String> params, HttpMethod httpMethod) {
        Date date = Date.from(Instant.now().plus(Duration.ofDays(expires)));
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName);
        request.withExpiration(date).withMethod(convertMethod(httpMethod));
        if (params != null) {
            params.forEach(request::addRequestParameter);
        }
        URL url = amazonS3.generatePresignedUrl(request);
        return url.toString();
    }

    /**
     * 转换请求
     */
    private com.amazonaws.HttpMethod convertMethod(HttpMethod httpMethod) {
        switch (httpMethod) {
            case GET:
                return com.amazonaws.HttpMethod.GET;
            case POST:
                return com.amazonaws.HttpMethod.POST;
            case PUT:
                return com.amazonaws.HttpMethod.PUT;
            case PATCH:
                return com.amazonaws.HttpMethod.PATCH;
            case HEAD:
                return com.amazonaws.HttpMethod.HEAD;
            case DELETE:
                return com.amazonaws.HttpMethod.DELETE;
            default:
                return com.amazonaws.HttpMethod.GET;
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        OssProperties properties = fileProperties.getOss();
        // 客户端配置
        ClientConfiguration clientConfiguration = new ClientConfiguration();
        clientConfiguration.setMaxConnections(properties.getMaxConnections());
        // 端点配置
        AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(
                properties.getEndpoint(), RegionUtils.getRegion(properties.getRegion()).getName());
        // 凭证配置
        AWSCredentials awsCredentials = new BasicAWSCredentials(properties.getAccessKey(),
                properties.getSecretKey());
        //创建亚马逊云管理对象
        this.amazonS3 = AmazonS3Client.builder()
                .withEndpointConfiguration(endpointConfiguration)
                .withClientConfiguration(clientConfiguration)
                .withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
                .disableChunkedEncoding()
                .withPathStyleAccessEnabled(properties.getPathStyleAccess())
                .build();
    }


}
